//==========================================
// Function : Asynchronous FIFO (w/ 2 asynchronous clocks).
// Coder    : zzt.
// Date     : 3/7/2020.
// Notes    : This is my asyncFIFO...
//=========================================


//地址位宽大于或者等于2的

`timescale 1ns/1ps

module My_AsyncFifo
  #(parameter    DATA_WIDTH    = 32,
                 ADDRESS_WIDTH = 2,
                 FIFO_DEPTH    = (1 << ADDRESS_WIDTH))
     //Reading port
    (output reg  [DATA_WIDTH-1:0]        Data_out, 
     output reg                          Empty_out,
     input wire                          Rd_en,
     input wire                          RClk,        
     //Writing port.	 
     input wire  [DATA_WIDTH-1:0]        Data_in,  
     output reg                          Full_out,
     input wire                          Wr_en,
     input wire                          WClk,
	 
     input wire                          Rst_n);

    /////Internal connections & variables//////
    reg   [DATA_WIDTH-1   :0]           Mem [FIFO_DEPTH-1:0]; 
    reg   [ADDRESS_WIDTH  :0]           Rd_pt_binary, Wr_pt_binary;
    //先用格雷码计数器，然后变成低位传递给二进制码进行读写
    reg   [ADDRESS_WIDTH  :0]           Rd_pt_sync_1, Rd_pt_sync_2, Wr_pt_sync_1, Wr_pt_sync_2;

    wire  [ADDRESS_WIDTH  :0]           Rd_pt_gray, Wr_pt_gray;
    wire  [ADDRESS_WIDTH-1:0]           Rd_addr, Wr_addr;


    assign Rd_addr = Rd_pt_binary[ADDRESS_WIDTH-1:0];
    assign Wr_addr = Wr_pt_binary[ADDRESS_WIDTH-1:0];
    
    //////////////Code///////////////
    always @ (posedge RClk) 
    begin:Data_out_latch
        if (Rd_en & !Empty_out) 
            Data_out <= Mem[Rd_addr];
    end
            
    //'Data_in' logic:
    always @ (posedge WClk)
    begin:Data_in_logic   
        if (Wr_en & !Full_out) 
            Mem[Wr_addr] <= Data_in;
    end

    always @ (posedge RClk or negedge Rst_n)
    begin:Rd_pt_binary_latch
        if (!Rst_n) 
            Rd_pt_binary <= 3'b0;  
        else if (Rd_en & !Full_out) 
            Rd_pt_binary <= Rd_pt_binary + 1'b1;
    end

    always @ (posedge WClk or negedge Rst_n)
    begin:Wr_pt_binary_latch
        if (!Rst_n) 
            Wr_pt_binary <= 3'b0;  
        else if (Wr_en & !Full_out)
            Wr_pt_binary <= Wr_pt_binary + 1'b1;
    end

    assign Rd_pt_gray = {Rd_pt_binary[ADDRESS_WIDTH], Rd_pt_binary[ADDRESS_WIDTH-1:0] ^ Rd_pt_binary[ADDRESS_WIDTH:1]};
    assign Wr_pt_gray = {Wr_pt_binary[ADDRESS_WIDTH], Wr_pt_binary[ADDRESS_WIDTH-1:0] ^ Wr_pt_binary[ADDRESS_WIDTH:1]};

    //Rd_pt_sync
    always @ (posedge WClk or negedge Rst_n)
    begin:Rd_pt_sync_1_logic
        if (!Rst_n) Rd_pt_sync_1 <= 3'b0;  
        else Rd_pt_sync_1 <= Rd_pt_gray;
    end

    always @ (posedge WClk or negedge Rst_n)
    begin:Rd_pt_sync_2_logic
        if (!Rst_n) Rd_pt_sync_2 <= 3'b0;  
        else Rd_pt_sync_2 <= Rd_pt_sync_1;
    end

    //Wr_pt_sync
    always @ (posedge RClk or negedge Rst_n)
    begin:Wr_pt_sync_1_logic
        if (!Rst_n) Wr_pt_sync_1 <= 3'b0;  
        else Wr_pt_sync_1 <= Wr_pt_gray;
    end

    always @ (posedge RClk or negedge Rst_n)
    begin:Wr_pt_sync_2_logic
        if (!Rst_n) Wr_pt_sync_2 <= 3'b0;  
        else Wr_pt_sync_2 <= Wr_pt_sync_1;
    end

    always @ (posedge WClk or negedge Rst_n)
    begin:Full_out_logic
        if (!Rst_n)
            Full_out <= 1'b0;
        else if ((Rd_pt_gray[ADDRESS_WIDTH -: 1] == ~Wr_pt_sync_2[ADDRESS_WIDTH -: 1]) && 
                    (Rd_pt_gray[ADDRESS_WIDTH-2:0] == Wr_pt_sync_2[ADDRESS_WIDTH-2:0]))
            Full_out <= 1'b1;
        else
            Full_out <= 1'b0;
    end

    always @ (posedge RClk or negedge Rst_n)
    begin:Empty_out_logic
        if (!Rst_n)
            Empty_out <= 1'b1;
        else if (Wr_pt_gray == Rd_pt_sync_2)
            Empty_out <= 1'b1;
        else
            Empty_out <= 1'b0;
    end

//缩码比较法
    // always @ (posedge WClk or negedge Rst_n)
    // begin:Full_out_logic
    //     if (!Rst_n)
    //         Full_out <= 1'b0;
    //     else if ((Wr_pt_gray[ADDRESS_WIDTH -: 1] == Rd_pt_sync_2[ADDRESS_WIDTH -: 1]) && 
    //                 ({Wr_pt_gray[ADDRESS_WIDTH] ^ Wr_pt_gray[ADDRESS_WIDTH-1], Wr_pt_gray[ADDRESS_WIDTH-2:0]} == 
    //                     {Rd_pt_sync_2[ADDRESS_WIDTH] ^ Rd_pt_sync_2[ADDRESS_WIDTH-1], Rd_pt_sync_2[ADDRESS_WIDTH-2:0]}))
    //         Full_out <= 1'b1;
    //     else
    //         Full_out <= 1'b0;
    // end

    // always @ (posedge RClk or negedge Rst_n)
    // begin:Empty_out_logic
    //     if (!Rst_n)
    //         Empty_out <= 1'b1;
    //     else if ((Rd_pt_gray[ADDRESS_WIDTH -: 1] == ~Wr_pt_sync_2[ADDRESS_WIDTH -: 1]) && 
    //                 ({Rd_pt_gray[ADDRESS_WIDTH] ^ Rd_pt_gray[ADDRESS_WIDTH-1], Rd_pt_gray[ADDRESS_WIDTH-2:0]} == 
    //                     {Wr_pt_sync_2[ADDRESS_WIDTH] ^ Wr_pt_sync_2[ADDRESS_WIDTH-1], Wr_pt_sync_2[ADDRESS_WIDTH-2:0]}))
    //         Empty_out <= 1'b1;
    //     else
    //         Empty_out <= 1'b0;

endmodule